package map;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import objects.Enemy;
import objects.EnemyType;

import tools.Coordinate;
import tools.Direction;

public class LevelFactory {

	private static LevelFactory MAP_FACTORY;

	public static LevelFactory getInstance() {
		if (MAP_FACTORY == null) {
			MAP_FACTORY = new LevelFactory();
		}
		return MAP_FACTORY;
	}

	private LevelFactory() {

	}

	public Level generateLevel(int number) {

		int height = 12;
		int width = 17;

		Level level = new Level(width, height, number);
		Tile[][] tiles = new Tile[width][height];

		Random random = new Random();

		LinkedList<Enemy> listEnemies = new LinkedList<Enemy>();

		for (int x = 0; x < tiles.length; x++) {

			for (int y = 0; y < tiles[x].length; y++) {

				if (x == 0 || x == tiles.length - 1 || y == 0
						|| y == tiles[x].length - 1) {
					tiles[x][y] = new Tile(TileType.WALL);
				} else {
					if (x == 8 && y == 1) {
						tiles[x][y] = new Tile(TileType.STAIRS_UP);
					} else if (x == 8 && y == tiles[x].length - 2 && number > 1) {
						tiles[x][y] = new Tile(TileType.STAIRS_DOWN);
					} else {
						tiles[x][y] = new Tile(TileType.GENERAL_FLOOR);

						if (random.nextInt(100) < 1) {
							if (random.nextInt(100) < 1) {
								listEnemies
										.add(new Enemy(x, y, EnemyType.BOSS));
							} else {
								listEnemies.add(new Enemy(x, y,
										EnemyType.NORMAL));
							}
						}
					}
				}

			}

		}

		level.setTiles(tiles);
		level.setListEnemies(listEnemies);

		return level;
	}

	public Level generateTown() {

		Level level = new Level(20, 20, 1);

		Random random = new Random();
		
		for (int x = 0; x < level.getTiles().length; x++) {
			for (int y = 0; y < level.getTiles()[x].length; y++) {
				Tile tile = new Tile(TileType.GRASS);
				if(random.nextInt(100) < 20) {
					tile.setTileType(TileType.FLOWER);
				}
				
				if ((x == 5 && y == 4) || (x == 5 && y == 6)
						|| (x == 4 && y == 4) || (x == 4 && y == 6)
						|| (x == 6 && y == 4) || (x == 6 && y == 6)
						|| (x == 4 && y == 5) || (x == 6 && y == 5)) {
					tile.setTileType(TileType.FLOOR2);
				} else if (x == 5 && y == 5) {
					tile.setTileType(TileType.STAIRS_DOWN);
				}

				tile.setHidden(false);
				level.getTiles()[x][y] = tile;

			}
		}

		return level;

	}

	public Level generateRoomAtPos(int width, int height, int number) {

		Level level = new Level(width, height, number);
		level.getListEnemies().clear();

		int maxRoomSize = 5;
		int minRoomSize = 4;
		Random random = new Random();

		boolean stairsPlaced = false;

		List<Room> listRooms = new LinkedList<Room>();

		int stairRoomIndex = random
				.nextInt((level.getTiles().length / maxRoomSize)
						* (level.getTiles()[0].length / maxRoomSize));

		for (int x1 = 0; x1 < level.getTiles().length; x1 += maxRoomSize) {
			for (int y1 = 0; y1 < level.getTiles()[x1].length; y1 += maxRoomSize) {

				int roomSize = random.nextInt(maxRoomSize - minRoomSize)
						+ minRoomSize;
				listRooms.add(new Room(new Coordinate(x1, y1), roomSize,
						roomSize));

				for (int x2 = x1; x2 < x1 + roomSize; x2++) {
					for (int y2 = y1; y2 < y1 + roomSize; y2++) {
						if (x2 == x1 || x2 == (x1 + roomSize) - 1 || y2 == y1
								|| y2 == (y1 + roomSize) - 1) {
							level.getTiles()[x2][y2] = new Tile(TileType.WALL);
						} else {
							if (stairRoomIndex == listRooms.size() - 1
									&& !stairsPlaced) {
								level.getTiles()[x2][y2] = new Tile(
										TileType.STAIRS_DOWN);
								stairsPlaced = true;
							} else {
								level.getTiles()[x2][y2] = new Tile(
										TileType.GENERAL_FLOOR);
								if (random.nextInt(100) < 50) {
									level.getListEnemies()
											.add(
													new Enemy(x2, y2,
															EnemyType.NORMAL));
								}
							}

						}

					}

				}

			}
		}

		boolean allConnected = false;

		while (!allConnected) {
			for (int i = 0; i < listRooms.size(); i++) {

				List<Direction> listDir = new ArrayList<Direction>();

				if (listRooms.get(i).getCoord().getY() == 0) {
					if (listRooms.get(i).getCoord().getX() == 0) {
						// kann nur nach rechts oder unten
						int dirInt = random.nextInt(2);
						if (dirInt == 0) {
							listDir.add(Direction.RIGHT);
						} else {
							listDir.add(Direction.DOWN);
						}

					} else if (listRooms.get(i).getCoord().getX() == width
							- maxRoomSize) {
						// kann nach links oder nach unten#
						int dirInt = random.nextInt(2);
						if (dirInt == 0) {
							listDir.add(Direction.LEFT);
						} else {
							listDir.add(Direction.DOWN);
						}

					} else {
						// kann nach links, rechts und unten
						int dirInt = random.nextInt(3);
						if (dirInt == 0) {
							listDir.add(Direction.RIGHT);
						} else if (dirInt == 1) {
							listDir.add(Direction.LEFT);
						} else {
							listDir.add(Direction.DOWN);
						}
					}
				} else if (listRooms.get(i).getCoord().getY() == height
						- maxRoomSize) {
					if (listRooms.get(i).getCoord().getX() == 0) {
						// kann nur nach rechts oder oben
						int dirInt = random.nextInt(2);
						if (dirInt == 0) {
							listDir.add(Direction.RIGHT);
						} else {
							listDir.add(Direction.UP);
						}

					} else if (listRooms.get(i).getCoord().getX() == width
							- maxRoomSize) {
						int dirInt = random.nextInt(2);
						// kann nach links oder nach oben
						if (dirInt == 0) {
							listDir.add(Direction.UP);
						} else {
							listDir.add(Direction.LEFT);
						}
					} else {
						// kann nach links, rechts und oben
						int dirInt = random.nextInt(3);
						if (dirInt == 0) {
							listDir.add(Direction.RIGHT);
						} else if (dirInt == 1) {
							listDir.add(Direction.LEFT);
						} else {
							listDir.add(Direction.UP);
						}
					}
				} else {
					if (listRooms.get(i).getCoord().getX() == 0) {
						// kann nur nach rechts oder unten oder oben
						int dirInt = random.nextInt(3);
						if (dirInt == 0) {
							listDir.add(Direction.RIGHT);
						} else if (dirInt == 1) {
							listDir.add(Direction.DOWN);
						} else {
							listDir.add(Direction.UP);
						}
					} else if (listRooms.get(i).getCoord().getX() == width
							- maxRoomSize) {
						// kann nach links oder nach unten oder oben
						int dirInt = random.nextInt(3);
						if (dirInt == 0) {
							listDir.add(Direction.DOWN);
						} else if (dirInt == 1) {
							listDir.add(Direction.UP);
						} else {
							listDir.add(Direction.LEFT);
						}
					} else {
						// kann nach links, rechts und unten und oben
						int dirInt = random.nextInt(4);
						if (dirInt == 0) {
							listDir.add(Direction.RIGHT);
						} else if (dirInt == 1) {
							listDir.add(Direction.LEFT);
						} else if (dirInt == 2) {
							listDir.add(Direction.UP);
						} else {
							listDir.add(Direction.DOWN);
						}
					}
				}

				for (Direction dir : listDir) {
					if (dir == Direction.RIGHT) {
						// Verbindung nach rechts
						int nextRoomIndex = 0;
						for (int n = 0; n < listRooms.size(); n++) {
							if (listRooms.get(n).getCoord().getY() == listRooms
									.get(i).getCoord().getY()
									&& listRooms.get(n).getCoord().getX() == listRooms
											.get(i).getCoord().getX()
											+ maxRoomSize) {
								nextRoomIndex = n;
							}
						}
						for (int n = -1; n < maxRoomSize
								- listRooms.get(i).getWidth() + 1; n++) {
							level.getTiles()[listRooms.get(i).getCoord().getX()
									+ listRooms.get(i).getWidth() + n][listRooms
									.get(i).getCoord().getY()
									+ (listRooms.get(i).getHeight() / 2)] = new Tile(
									TileType.GENERAL_FLOOR);
							// Wall
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									+ listRooms.get(i).getWidth() + n][listRooms
									.get(i).getCoord().getY()
									+ (listRooms.get(i).getHeight() / 2) - 1] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										+ listRooms.get(i).getWidth() + n][listRooms
										.get(i).getCoord().getY()
										+ (listRooms.get(i).getHeight() / 2)
										- 1] = new Tile(TileType.WALL);
							}
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									+ listRooms.get(i).getWidth() + n][listRooms
									.get(i).getCoord().getY()
									+ (listRooms.get(i).getHeight() / 2) + 1] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										+ listRooms.get(i).getWidth() + n][listRooms
										.get(i).getCoord().getY()
										+ (listRooms.get(i).getHeight() / 2)
										+ 1] = new Tile(TileType.WALL);
							}
						}
						listRooms.get(i).setConnected(true);
						listRooms.get(i).setConnectionCount(
								listRooms.get(i).getConnectionCount() + 1);
						listRooms.get(nextRoomIndex).setConnected(true);

					} else if (dir == Direction.DOWN) {
						// Verbindung nach unten
						int nextRoomIndex = 0;
						for (int n = 0; n < listRooms.size(); n++) {
							if (listRooms.get(n).getCoord().getY() == listRooms
									.get(i).getCoord().getY()
									+ maxRoomSize
									&& listRooms.get(n).getCoord().getX() == listRooms
											.get(i).getCoord().getX()) {
								nextRoomIndex = n;
							}
						}
						for (int n = -1; n < maxRoomSize
								- listRooms.get(i).getHeight() + 1; n++) {
							level.getTiles()[listRooms.get(i).getCoord().getX()
									+ (listRooms.get(i).getWidth() / 2)][listRooms
									.get(i).getCoord().getY()
									+ listRooms.get(i).getHeight() + n] = new Tile(
									TileType.GENERAL_FLOOR);
							// Wall
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									+ (listRooms.get(i).getWidth() / 2) - 1][listRooms
									.get(i).getCoord().getY()
									+ listRooms.get(i).getHeight() + n] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										+ (listRooms.get(i).getWidth() / 2) - 1][listRooms
										.get(i).getCoord().getY()
										+ listRooms.get(i).getHeight() + n] = new Tile(
										TileType.WALL);
							}
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									+ (listRooms.get(i).getWidth() / 2) + 1][listRooms
									.get(i).getCoord().getY()
									+ listRooms.get(i).getHeight() + n] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										+ (listRooms.get(i).getWidth() / 2) + 1][listRooms
										.get(i).getCoord().getY()
										+ listRooms.get(i).getHeight() + n] = new Tile(
										TileType.WALL);
							}

						}
						listRooms.get(i).setConnected(true);
						listRooms.get(i).setConnectionCount(
								listRooms.get(i).getConnectionCount() + 1);
						listRooms.get(nextRoomIndex).setConnected(true);
					} else if (dir == Direction.LEFT) {
						// Verbindung nach links
						int lastRoomIndex = 0;
						for (int n = 0; n < listRooms.size(); n++) {
							if (listRooms.get(n).getCoord().getY() == listRooms
									.get(i).getCoord().getY()
									&& listRooms.get(n).getCoord().getX() == listRooms
											.get(i).getCoord().getX()
											- maxRoomSize) {
								lastRoomIndex = n;
							}
						}
						for (int n = 0; listRooms.get(i).getCoord().getX() - n >= listRooms
								.get(lastRoomIndex).getCoord().getX()
								+ listRooms.get(lastRoomIndex).getWidth() - 1; n++) {
							level.getTiles()[listRooms.get(i).getCoord().getX()
									- n][listRooms.get(i).getCoord().getY()
									+ (listRooms.get(i).getHeight() / 2)] = new Tile(
									TileType.GENERAL_FLOOR);
							// Wall
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									- n][listRooms.get(i).getCoord().getY()
									+ (listRooms.get(i).getHeight() / 2) - 1] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										- n][listRooms.get(i).getCoord().getY()
										+ (listRooms.get(i).getHeight() / 2)
										- 1] = new Tile(TileType.WALL);
							}
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									- n][listRooms.get(i).getCoord().getY()
									+ (listRooms.get(i).getHeight() / 2) + 1] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										- n][listRooms.get(i).getCoord().getY()
										+ (listRooms.get(i).getHeight() / 2)
										+ 1] = new Tile(TileType.WALL);
							}
						}
						listRooms.get(i).setConnected(true);
						listRooms.get(i).setConnectionCount(
								listRooms.get(i).getConnectionCount() + 1);
						listRooms.get(lastRoomIndex).setConnected(true);
					} else if (dir == Direction.UP) {
						// Verbindung nach rechts
						int lastRoomIndex = 0;
						for (int n = 0; n < listRooms.size(); n++) {
							if (listRooms.get(n).getCoord().getY() == listRooms
									.get(i).getCoord().getY()
									- maxRoomSize
									&& listRooms.get(n).getCoord().getX() == listRooms
											.get(i).getCoord().getX()) {
								lastRoomIndex = n;
							}
						}
						for (int n = 0; listRooms.get(i).getCoord().getY() - n >= listRooms
								.get(lastRoomIndex).getCoord().getY()
								+ listRooms.get(lastRoomIndex).getHeight() - 1; n++) {
							level.getTiles()[listRooms.get(i).getCoord().getX()
									+ (listRooms.get(i).getWidth() / 2)][listRooms
									.get(i).getCoord().getY()
									- n] = new Tile(TileType.GENERAL_FLOOR);
							// Wall
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									+ (listRooms.get(i).getWidth() / 2) - 1][listRooms
									.get(i).getCoord().getY()
									- n] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										+ (listRooms.get(i).getWidth() / 2) - 1][listRooms
										.get(i).getCoord().getY()
										- n] = new Tile(TileType.WALL);
							}
							if (level.getTiles()[listRooms.get(i).getCoord()
									.getX()
									+ (listRooms.get(i).getWidth() / 2) + 1][listRooms
									.get(i).getCoord().getY()
									- n] == null) {
								level.getTiles()[listRooms.get(i).getCoord()
										.getX()
										+ (listRooms.get(i).getWidth() / 2) + 1][listRooms
										.get(i).getCoord().getY()
										- n] = new Tile(TileType.WALL);
							}

						}
						listRooms.get(i).setConnected(true);
						listRooms.get(i).setConnectionCount(
								listRooms.get(i).getConnectionCount() + 1);
						listRooms.get(lastRoomIndex).setConnected(true);
					}
				}

				allConnected = true;
				for (Room listRoom : listRooms) {
					if (listRoom.getConnectionCount() < 2) {
						allConnected = false;
						break;
					}
				}

			}
		}

		return level;
	}

}
